/* Copyright (c) 2011-2011 by Tian Yantao (yantaotian@foxmail.com).
 * Published under the Clear BSD license.
 * Map data copyright (c) Google inc.
 * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
 * full text of the license. */

/**
 * -requires OpenLayers/Layer/Grid.js
 * -requires OpenLayers/Tile/Image.js
 */

/** 
 * Class: OpenLayers.Layer.WeCelestial
 * 
 * Inherits from:
 *  - <OpenLayers.Layer.Grid>
 */
OpenLayers.Layer.WeCelestial = OpenLayers.Class(OpenLayers.Layer.Grid, {
    
    /**
     * APIProperty: isBaseLayer
     * Default is true, as this is designed to be a base tile source. 
     */
    isBaseLayer: true,
    
    /**
     * APIProperty: sphericalMecator
     * Whether the tile extents should be set to the defaults for 
     *    spherical mercator.
     */
    sphericalMercator: true,
    
    wrapDateLine: true,

    /**
     * APIProperty: zoomOffset
     * {Number} If your cache has more zoom levels than you want to provide
     *     access to with this layer, supply a zoomOffset.  This zoom offset
     *     is added to the current map zoom level to determine the level
     *     for a requested tile.  For example, if you supply a zoomOffset
     *     of 3, when the map is at the zoom 0, tiles will be requested from
     *     level 3 of your cache.  Default is 0 (assumes cache level and map
     *     zoom are equivalent).  Using <zoomOffset> is an alternative to
     *     setting <serverResolutions> if you only want to expose a subset
     *     of the server resolutions.
     */
    zoomOffset: 0,
    
    /**
     * APIProperty: serverResolutions
     * {Array} A list of all resolutions available on the server.  Only set this
     *     property if the map resolutions differs from the server.
     */
    serverResolutions: null,

    /**
     * Constructor: OpenLayers.Layer.Celestial
     *
     * Parameters:
     * name - {String}
     * url - {String}
     * options - {Object} Hashtable of extra options to tag onto the layer
     */
    initialize: function(name, url, options) {
        if (options && options.sphericalMercator || this.sphericalMercator) {
            options = OpenLayers.Util.extend({
                maxExtent: new OpenLayers.Bounds(
                    -128 * 156543.0339,
                    -128 * 156543.0339,
                    128 * 156543.0339,
                    128 * 156543.0339
                ),
                maxResolution: 156543.0339,
                numZoomLevels: 13,
                units: "m",
                projection: "EPSG:900913"
            }, options);
        }
        url = url || this.url;
        name = name || this.name;
        var newArguments = [name, url, {}, options];
        OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
    },
    
    /**
     * APIMethod: clone
     * Create a clone of this layer
     *
     * Parameters:
     * obj - {Object} Is this ever used?
     * 
     * Returns:
     * {<OpenLayers.Layer.WeCelestial>} An exact clone of this OpenLayers.Layer.WeCelestial
     */
    clone: function (obj) {
        
        if (obj == null) {
            obj = new OpenLayers.Layer.WeCelestial(this.name,
                                            this.url,
                                            this.getOptions());
        }

        //get all additions from superclasses
        obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);

        return obj;
    },    

    /**
     * Method: getURL
     *
     * Parameters:
     * bounds - {<OpenLayers.Bounds>}
     *
     * Returns:
     * {String} A string with the layer's url and parameters and also the
     *          passed-in bounds and appropriate tile size specified as
     *          parameters
     */
    getURL: function (bounds) {
        return "";
    },
    
    /**
     * Method: getXYZ
     * Calculates x, y and z for the given bounds.
     *
     * Parameters:
     * bounds - {<OpenLayers.Bounds>}
     *
     * Returns:
     * {Object} - an object with x, y and z properties.
     */
    getXYZ: function(bounds) {
        var res = this.map.getResolution();
        var x = Math.round((bounds.left - this.maxExtent.left) 
            / (res * this.tileSize.w));
        var y = Math.round((this.maxExtent.top - bounds.top) 
            / (res * this.tileSize.h));
        var z = this.serverResolutions != null ?
            OpenLayers.Util.indexOf(this.serverResolutions, res) :
            this.map.getZoom() + this.zoomOffset;
            
        var limit = Math.pow(2, z);
        if (this.wrapDateLine) {
           x = ((x % limit) + limit) % limit;
        }

        return {'x': x, 'y': y, 'z': z};
    },
    
    /**
     * Method: addTile
     * addTile creates a tile, initializes it, and adds it to the layer div. 
     * 
     * Parameters:
     * bounds - {<OpenLayers.Bounds>}
     * position - {<OpenLayers.Pixel>}
     * 
     * Returns:
     * {<OpenLayers.Tile.Image>} The added OpenLayers.Tile.Image
     */
    addTile:function(bounds,position) {
        return new OpenLayers.Tile.Image(this, position, bounds, 
                                         null, this.tileSize);
    },
     
    /* APIMethod: setMap
     * When the layer is added to a map, then we can fetch our origin 
     *    (if we don't have one.) 
     * 
     * Parameters:
     * map - {<OpenLayers.Map>}
     */
    setMap: function(map) {
        OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);
        if (!this.tileOrigin) { 
            this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left,
                                                this.maxExtent.bottom);
        }                                       
    },

    CLASS_NAME: "OpenLayers.Layer.WeCelestial"
});


/**
 * Class: OpenLayers.Layer.WeMoon
 * A class to access NASA moon images.
 * 
 * Inherits from:
 *  - <OpenLayers.Layer.WeCelestial>
 */
OpenLayers.Layer.WeMoon = OpenLayers.Class(OpenLayers.Layer.WeCelestial, {
    attribution: "Image Data - <a href='http://www.google.com/sky'>Google Sky</a>",
    url: "http://mw1.google.com/mw-planetary/lunar/lunarmaps_v1/clem_bw/",
    
    initialize: function(name, url, options) {
        OpenLayers.Layer.WeCelestial.prototype.initialize.apply(this, arguments);
        this.name = name ? name : OpenLayers.i18n('WeMoon_name');
    },
    
    getURL: function(bounds) {
        var xyz = this.getXYZ(bounds);
        var bound = Math.pow(2, xyz.z);
        return this.url + xyz.z + "/" + xyz.x + "/" + (bound - xyz.y - 1) + '.jpg';
    },
    
    clone: function(obj) {
        if (obj == null) {
            obj = new OpenLayers.Layer.WeMoon(
                this.name, this.url, this.getOptions());
        }
        obj = OpenLayers.Layer.WeCelestial.prototype.clone.apply(this, [obj]);
        return obj;
    },
    CLASS_NAME: "OpenLayers.Layer.WeMoon"
});

/**
 * Class: OpenLayers.Layer.WeMars
 * A class to access NASA Mars visible/infrared/elevation images.
 * 
 * Inherits from:
 *  - <OpenLayers.Layer.WeCelestial>
 */
OpenLayers.Layer.WeMars = OpenLayers.Class(OpenLayers.Layer.WeCelestial, {
    attribution: "Image Data - <a href='http://www.google.com/sky'>Google Sky</a>",
    url: "visible",
    
    initialize: function(name, url, options) {
        OpenLayers.Layer.WeCelestial.prototype.initialize.apply(this, arguments);
        this.name = name ? name : OpenLayers.i18n('WeMars_name');
    },
    
    getURL: function(bounds) {
        var xyz = this.getXYZ(bounds);
        var bound = Math.pow(2, xyz.z);
        var x = xyz.x;
        var y = xyz.y;
        var quads = ['t'];
        for (var z = 0; z < xyz.z; z++) {
            bound = bound / 2;
            if (y < bound) {
                if (x < bound) {
                    quads.push('q');
                } else {
                    quads.push('r');
                    x -= bound;
                }
            } else {
                if (x < bound) {
                    quads.push('t');
                    y -= bound;
                } else {
                    quads.push('s');
                    x -= bound;
                    y -= bound;
                }
            }
        }
        return "http://mw1.google.com/mw-planetary/mars/" + this.url + "/" + quads.join('') + ".jpg";
    },
    
    clone: function(obj) {
        if (obj == null) {
            obj = new OpenLayers.Layer.WeMoon(
                this.name, this.url, this.getOptions());
        }
        obj = OpenLayers.Layer.WeCelestial.prototype.clone.apply(this, [obj]);
        return obj;
    },
    CLASS_NAME: "OpenLayers.Layer.WeMars"
});

/**
 * Class: OpenLayers.Layer.WeSky
 * A class to exploring sky.
 * 
 * Inherits from:
 *  - <OpenLayers.Layer.WeCelestial>
 */
OpenLayers.Layer.WeSky = OpenLayers.Class(OpenLayers.Layer.WeCelestial, {
    attribution: "Image Data - <a href='http://www.google.com/sky'>Google Sky</a>",
    url: "http://mw1.google.com/mw-planetary/sky/skytiles_v1/",
    
    initialize: function(name, url, options) {
        OpenLayers.Layer.WeCelestial.prototype.initialize.apply(this, arguments);
        this.name = name ? name : OpenLayers.i18n('WeSky_name');
    },
    
    getURL: function(bounds) {
        var xyz = this.getXYZ(bounds);
        return this.url + xyz.x + "_" + xyz.y + '_' + xyz.z + '.jpg';
    },
    
    clone: function(obj) {
        if (obj == null) {
            obj = new OpenLayers.Layer.WeMoon(
                this.name, this.url, this.getOptions());
        }
        obj = OpenLayers.Layer.WeCelestial.prototype.clone.apply(this, [obj]);
        return obj;
    },
    CLASS_NAME: "OpenLayers.Layer.WeSky"
});
